use crate::errors::Result;
use crate::models::*;
use crate::services::resource_service::ResourceService;
use crate::services::article_service::ArticleService;
use crate::services::product_service::ProductService;
use actix_multipart::Multipart;
use actix_web::{delete, get, post, put, web, HttpResponse};
use actix_web::web::Query;
use sea_orm::DatabaseConnection;
use utoipa::path;
use uuid::Uuid;
use futures_util::TryStreamExt;

// Resource management endpoints

#[utoipa::path(
    post,
    path = "/admin/resources/upload",
    tag = "Admin Resources",
    operation_id = "upload_resources",
    request_body(content = Vec<u8>, content_type = "multipart/form-data", description = "Files to upload"),
    responses(
        (status = 200, description = "Files uploaded successfully", body = UploadResponse),
        (status = 400, description = "Invalid request"),
        (status = 401, description = "Unauthorized")
    )
)]
#[post("/resources/upload")]
pub async fn upload_resources(
    db: web::Data<DatabaseConnection>,
    payload: Multipart,
) -> Result<HttpResponse> {
    let upload_dir = "uploads";
    let response = ResourceService::upload_files(&db, payload, upload_dir).await?;
    Ok(HttpResponse::Ok().json(response))
}

#[utoipa::path(
    delete,
    path = "/admin/resources/{id}",
    tag = "Admin Resources",
    operation_id = "delete_resource",
    params(
        ("id" = Uuid, Path, description = "Resource ID")
    ),
    responses(
        (status = 204, description = "Resource deleted successfully"),
        (status = 404, description = "Resource not found"),
        (status = 401, description = "Unauthorized")
    )
)]
#[delete("/resources/{id}")]
pub async fn delete_resource(
    db: web::Data<DatabaseConnection>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse> {
    let id = path.into_inner();
    ResourceService::delete_resource_cascade(&db, id).await?;
    Ok(HttpResponse::NoContent().finish())
}

#[utoipa::path(
    get,
    path = "/admin/resources/{id}",
    tag = "Admin Resources",
    operation_id = "get_resource",
    params(
        ("id" = Uuid, Path, description = "Resource ID")
    ),
    responses(
        (status = 200, description = "Resource retrieved successfully", body = Resource),
        (status = 404, description = "Resource not found")
    )
)]
#[get("/resources/{id}")]
pub async fn get_resource(
    db: web::Data<DatabaseConnection>,
    path: web::Path<Uuid>,
) -> Result<HttpResponse> {
    let id = path.into_inner();
    let resource = ResourceService::get_by_id(&db, id).await?;
    Ok(HttpResponse::Ok().json(resource))
}

// Article resource assignment endpoints

#[utoipa::path(
    post,
    path = "/admin/articles/{article_id}/resources/{language_code}",
    tag = "Admin Article Resources",
    operation_id = "assign_resource_to_article",
    params(
        ("article_id" = Uuid, Path, description = "Article ID"),
        ("language_code" = String, Path, description = "Language code")
    ),
    request_body = ResourceAssignmentRequest,
    responses(
        (status = 200, description = "Resource assigned successfully"),
        (status = 400, description = "Invalid request"),
        (status = 404, description = "Article or resource not found")
    )
)]
#[post("/articles/{article_id}/resources/{language_code}")]
pub async fn assign_resource_to_article(
    db: web::Data<DatabaseConnection>,
    path: web::Path<(Uuid, String)>,
    request: web::Json<ResourceAssignmentRequest>,
) -> Result<HttpResponse> {
    let (article_id, language_code) = path.into_inner();
    ResourceService::assign_to_article(&db, article_id, &language_code, request.into_inner()).await?;
    Ok(HttpResponse::Ok().json(serde_json::json!({"message": "Resource assigned successfully"})))
}

#[utoipa::path(
    delete,
    path = "/admin/articles/{article_id}/resources/{language_code}/{resource_id}",
    tag = "Admin Article Resources",
    operation_id = "remove_resource_from_article",
    params(
        ("article_id" = Uuid, Path, description = "Article ID"),
        ("language_code" = String, Path, description = "Language code"),
        ("resource_id" = Uuid, Path, description = "Resource ID")
    ),
    responses(
        (status = 200, description = "Resource removed successfully"),
        (status = 404, description = "Assignment not found")
    )
)]
#[delete("/articles/{article_id}/resources/{language_code}/{resource_id}")]
pub async fn remove_resource_from_article(
    db: web::Data<DatabaseConnection>,
    path: web::Path<(Uuid, String, Uuid)>,
) -> Result<HttpResponse> {
    let (article_id, language_code, resource_id) = path.into_inner();
    ResourceService::remove_from_article(&db, article_id, &language_code, resource_id).await?;
    Ok(HttpResponse::Ok().json(serde_json::json!({"message": "Resource removed successfully"})))
}

#[utoipa::path(
    get,
    path = "/admin/articles/{article_id}/resources",
    tag = "Admin Article Resources",
    operation_id = "get_article_resources",
    params(
        ("article_id" = Uuid, Path, description = "Article ID"),
        ("language_code" = Option<String>, Query, description = "Filter by language code")
    ),
    responses(
        (status = 200, description = "Resources retrieved successfully", body = Vec<ArticleResource>),
        (status = 404, description = "Article not found")
    )
)]
#[get("/articles/{article_id}/resources")]
pub async fn get_article_resources(
    db: web::Data<DatabaseConnection>,
    path: web::Path<Uuid>,
    query: Query<FilterOptions>,
) -> Result<HttpResponse> {
    let article_id = path.into_inner();
    let query_options = query.into_inner();
    let language_code = query_options.language_code.as_deref();
    let resources = ResourceService::get_article_resources(&db, article_id, language_code).await?;
    Ok(HttpResponse::Ok().json(resources))
}

// Product resource assignment endpoints

#[utoipa::path(
    post,
    path = "/admin/products/{product_id}/resources/{language_code}",
    tag = "Admin Product Resources",
    operation_id = "assign_resource_to_product",
    params(
        ("product_id" = Uuid, Path, description = "Product ID"),
        ("language_code" = String, Path, description = "Language code")
    ),
    request_body = ResourceAssignmentRequest,
    responses(
        (status = 200, description = "Resource assigned successfully"),
        (status = 400, description = "Invalid request"),
        (status = 404, description = "Product or resource not found")
    )
)]
#[post("/products/{product_id}/resources/{language_code}")]
pub async fn assign_resource_to_product(
    db: web::Data<DatabaseConnection>,
    path: web::Path<(Uuid, String)>,
    request: web::Json<ResourceAssignmentRequest>,
) -> Result<HttpResponse> {
    let (product_id, language_code) = path.into_inner();
    ResourceService::assign_to_product(&db, product_id, &language_code, request.into_inner()).await?;
    Ok(HttpResponse::Ok().json(serde_json::json!({"message": "Resource assigned successfully"})))
}

#[utoipa::path(
    delete,
    path = "/admin/products/{product_id}/resources/{language_code}/{resource_id}",
    tag = "Admin Product Resources",
    operation_id = "remove_resource_from_product",
    params(
        ("product_id" = Uuid, Path, description = "Product ID"),
        ("language_code" = String, Path, description = "Language code"),
        ("resource_id" = Uuid, Path, description = "Resource ID")
    ),
    responses(
        (status = 200, description = "Resource removed successfully"),
        (status = 404, description = "Assignment not found")
    )
)]
#[delete("/products/{product_id}/resources/{language_code}/{resource_id}")]
pub async fn remove_resource_from_product(
    db: web::Data<DatabaseConnection>,
    path: web::Path<(Uuid, String, Uuid)>,
) -> Result<HttpResponse> {
    let (product_id, language_code, resource_id) = path.into_inner();
    ResourceService::remove_from_product(&db, product_id, &language_code, resource_id).await?;
    Ok(HttpResponse::Ok().json(serde_json::json!({"message": "Resource removed successfully"})))
}

#[utoipa::path(
    get,
    path = "/admin/products/{product_id}/resources",
    tag = "Admin Product Resources",
    operation_id = "get_product_resources",
    params(
        ("product_id" = Uuid, Path, description = "Product ID"),
        ("language_code" = Option<String>, Query, description = "Filter by language code")
    ),
    responses(
        (status = 200, description = "Resources retrieved successfully", body = Vec<ProductResource>),
        (status = 404, description = "Product not found")
    )
)]
#[get("/products/{product_id}/resources")]
pub async fn get_product_resources(
    db: web::Data<DatabaseConnection>,
    path: web::Path<Uuid>,
    query: Query<FilterOptions>,
) -> Result<HttpResponse> {
    let product_id = path.into_inner();
    let query_options = query.into_inner();
    let language_code = query_options.language_code.as_deref();
    let resources = ResourceService::get_product_resources(&db, product_id, language_code).await?;
    Ok(HttpResponse::Ok().json(resources))
}